#include <stdio.h>
#include <stdlib.h>
#include <ijpegd_common.h>

#define MAX_COMPS_IN_ONE_SCAN   4
int ijpegd_decode_header(ijpegd_t *h, uint8_t *jpegdbuf, int jpeglen)
{
	int i,j,n,v,c,rst,nm,code,hid;
	int dc,ht,abase,bbase,hbase,hb;
	int l[16];
	int pos = 0;

	char tmp[4];
	int  nTemp;

	int nblk[4] = {0};
	int qt_sel[4] = {0};
	int ha_sel[4] = {0};
	int hd_sel[4] = {0};
	int ncol=0, nrst=0;
	unsigned int width = 0, height = 0;
	h->bs_len = 0;

	/* Initialize h->Huffenc with default values */
	for(i=0;i<384;i++)
		h->huffenc[i]=0xfff;
	for(i=168,j=0xfd0;i<176;i++,j++)
		h->huffenc[i]=j;
	for(i=344,j=0xfd0;i<352;i++,j++)
		h->huffenc[i]=j;

	ht=nm=rst=0;
	for(i=162;i<174;i++)
		h->heb[i]=0;

	for(i=0;i<4;i++)
		for(j=0;j<64;j++)
			h->qt[i][j] = 0;

	unsigned char *org_bs = h->fbs;

	while(pos<jpeglen) {
		c=jpegdbuf[pos++];
		if(c!=0xff)
			continue;

		/* Found a potential marker */
		while((c=jpegdbuf[pos++])==0xff);
		if(c==0){
			nm++;
			continue; /* It wasn't a marker */
		}

		/* Which marker ? */
marker:
		switch(c){
			case 0xc0 : // Baseline (0, 0)
			case 0xc1 : // Ext. Sequential, Huffman (0, 0)
			case 0xc2 : // Progressive, Huffman (1, 0)
			case 0xc3 : // Lossless, Huffman
			case 0xc5 : // Differential Sequential, Huffman
			case 0xc6 : // Differential Progressive, Huffman
			case 0xc7 : // Differential Lossless, Huffman
			case 0xc9 : // Extended Sequential, Arithmetic (0, 1)
			case 0xca : // Progressive, Arithmetic (1, 1)
			case 0xcb : // Lossless, Huffman
			case 0xcd : // Differential Sequential, Arithmetic
			case 0xce : // Differential Progressive, Arithmetic
			case 0xcf : // Differential Lossless, Arithmetic
				pos += 3;
				v = jpegdbuf[pos++];
				height=(v<<8)|jpegdbuf[pos++];
				h->dst_h = height;
				v =jpegdbuf[pos++];
				width=(v<<8)|jpegdbuf[pos++];
				h->dst_w = width;
				ncol = n = jpegdbuf[pos++];
				ijpegd_log(h, C_LOG_DEBUG, "picture size: %d x %d [col: %d]\n", width, height, ncol);
				for(i=0;i<n;i++){
					pos++;
					nblk[i] = jpegdbuf[pos++];
					qt_sel[i] = jpegdbuf[pos++];
				}
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "SOF");
				ijpegd_log(h, C_LOG_DEBUG, "Read extra0x%02x - %d bytes\n", c, 8 + (n*3));
				break;

			case 0xc4 :
				/* DHT marker detected */
				ijpegd_log(h, C_LOG_DEBUG, "---------------------------------\n");
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "DHT"); // Huffman Table
				/* Get the lenght of the marker segment */
				// Lh : HT length (16b)
				v=jpegdbuf[pos++];
				n=(v<<8)|jpegdbuf[pos++];

				ijpegd_log(h, C_LOG_DEBUG, "Lh %d\n",n);
				/* Reduce marker segment byte count */
				n-=2;

				while(n) {

					/* Get the type of table */
					v=jpegdbuf[pos++]; // Tc & Th
					// Tc : Table class (4b)
					//      0 = DC or lossless table
					//      1 = AC table
					// Th : HT destination identifier
					// - specifies 1 of 4 possible destinations at the decoder into
					// which HT shall be installed.

					/* Reduce marker segment byte count */
					n--;

					ijpegd_log(h, C_LOG_DEBUG, "Tc %d\n",v>>4);
					ijpegd_log(h, C_LOG_DEBUG, "Th %d\n",v&15);
					hid= v>>4 ? 2 : 0;
					hid|=v&15 ? 1 : 0;
					switch(hid){
						case 1:
							hbase=368;
							break;
						case 2:
							hbase=0;
							break;
						case 3:
							hbase=176;
							break;
						default :
							hbase=352;
							break;
					}
					if((v>>4))
						abase=0;
					else
						abase=1;
					dc=abase;
					ht=v&15;
					abase|=ht<<1;
					switch(abase){
						case 1 : case 3 :
							bbase=162;
							break;
						case 2 :
							bbase=174;
							break;
						default :
							bbase=0;
							break;
					}
					abase<<=4;
					/* Memory initialization */
					for(i=abase;i<abase+16;i++)
						h->hea[i]=255;
					/* Get the number of codes for each length */
					// Lj : # of Huffman codes of length i
					// - specifies the # of Huffman codes for each of 16 possible lengths
					// allowed by spec. BITS
					for(i=0;i<16;i++) {
						l[i]=jpegdbuf[pos++];
					}
					/* Reduce marker segment byte count */

					n-=16;
					code=0;
					for(i=0;i<16;i++,abase++){
						h->min[abase]=code;
						h->hea[abase]=bbase-code;
						if(l[i])
							// Vi,j : associated with each Huffman code
							// - specifies, for each i the value associated with each Huffman code
							// of length i.  HUFFVAL
							for(j=0;j<l[i];j++,bbase++){
								v=jpegdbuf[pos++];
								/* Reduce marker segment byte count */
								n--;
								if(dc){
									h->huffenc[hbase+v]=(i<<8)|(code&0xff);
									v&=15;
									if(ht)
										v<<=4;
									h->heb[bbase]|=v;
								} else{
									if(v==0)
										hb=160;
									else if(v==0xf0)
										hb=161;
									else
										hb=(v>>4)*10+(v&0xf)-1;
									h->huffenc[hbase+hb]=(i<<8)|(code&0xff);
									h->heb[bbase]=v;
								}
								code++;
							}
						code<<=1;
					}
				}
				break;

			case 0xc8 :
				ijpegd_log(h, C_LOG_DEBUG, "---------------------------------\n");
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "JPG extensions !!!");
				exit(1);
				break;

			case 0xcc :
				ijpegd_log(h, C_LOG_DEBUG, "---------------------------------\n");
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "DAC !!! (arithmatic coding not supported)");
				exit(1);
				break;

			case 0xd0 :
			case 0xd1 :
			case 0xd2 :
			case 0xd3 :
			case 0xd4 :
			case 0xd5 :
			case 0xd6 :
			case 0xd7 :
				ijpegd_log(h, C_LOG_DEBUG, "---------------------------------\n");
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "RST");
				ijpegd_log(h, C_LOG_DEBUG, "reset %d\n",c&15);
				//nrst = c&15;
				rst++;
				break;

			case 0xd8 :
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "SOI");
				break;

			case 0xd9 :
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "EOI");

				//fputc(0xff,fpo);
				//fputc(0xd9,fpo);
				*org_bs = 0xff;
				org_bs++;
				h->bs_len++;
				*org_bs = 0xd9;
				org_bs++;
				h->bs_len++;
				break;

			case 0xda :
				ijpegd_log(h, C_LOG_DEBUG, "---------------------------------\n");
				ijpegd_log(h, C_LOG_DEBUG, "SOS\n"); // Start of Scan
				tmp[0] = jpegdbuf[pos++];
				tmp[1] = jpegdbuf[pos++];
				nTemp = (tmp[0] << 8) | (tmp[1]); //Ls (scan header length)
				n=jpegdbuf[pos++]; //Ns (# of image components)
				ijpegd_log(h, C_LOG_DEBUG, "Ls = 0x%d, Ns = %02x\n", nTemp, n);

				if ((nTemp != (n*2 + 6)) || (n < 1) || (n > MAX_COMPS_IN_ONE_SCAN)) {
					ijpegd_log(h, C_LOG_DEBUG, "bad scan length Ls = %d vs. (%d * 2 + 6)\n", nTemp, n);
				}

				for(i=0;i<n;i++){
					// Cs, Td&Ta
					pos++;
					ha_sel[i] = hd_sel[i] = jpegdbuf[pos++];
					ha_sel[i] = (ha_sel[i] & 0x30)>>4;
					hd_sel[i] = hd_sel[i] & 0x3;
				}
				pos++; //Ss
				pos++; //Se
				pos++; //Ah&Al
				//fpo=fopen("ecs.bin","wb");
				for(;;) {
					c = jpegdbuf[pos++];
					if( c == 0xff ) {
						c = jpegdbuf[pos++];
						if( (c!=0x00) && ((c&0xf8) !=0xd0) ) {
							//fclose(fpo);
							goto marker;
						} else {
							//fputc(0xff,fpo);
							*org_bs = 0xff;
							org_bs++;
							h->bs_len++;
						}
					}
					//fputc(c,fpo);
					*org_bs = (unsigned char)c;
					org_bs++;
					h->bs_len++;
				}
				break;

			case 0xdb :
				ijpegd_log(h, C_LOG_DEBUG, "---------------------------------\n");
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "DQT"); // Quantization Table
				// Lq : QT Length (16b)
				v=jpegdbuf[pos++];
				v=(v<<8)|jpegdbuf[pos++];
				ijpegd_log(h, C_LOG_DEBUG, "Lq %d\n",v);
				int len = v-2;
				while (len > 0) {
					int prec;
					v=jpegdbuf[pos++];
					// Pq : QT element precision (4b)
					// - specifies the precision of the Qk values.
					//   0 indicates 8-bits Qk values.
					//   1 indicates 16-bits Qk values
					ijpegd_log(h, C_LOG_DEBUG, "Pq %d\n",v>>4);
					prec = v >> 4;
					// Tq : QT destination identifier (4b)
					// - specifies one of 4 possible destnations at the decoder into
					// which the QT shall be installed.
					ijpegd_log(h, C_LOG_DEBUG, "Tq %d\n",v&15);
					n=v&15;
					if(n > 3){
						ijpegd_log(h, C_LOG_DEBUG, "error QT\n");
					}
					for(i=0;i<64;i++) {
						// Qk: Quantization table element
						// k is the index in the zigzag ordering of the DCT coeff
						// JPC only do 8-bit Qk! (ie, Pq shall be 0)
						if(prec){
							v=jpegdbuf[pos++];
							h->qt[n][i] = (v<<8)|jpegdbuf[pos++];
						} else
							h->qt[n][i] = jpegdbuf[pos++];
					}
					len -= 64+1;
					if (prec) len -= 64;
				}
				break;

			case 0xdd :
				ijpegd_log(h, C_LOG_DEBUG, "---------------------------------\n");
				ijpegd_log(h, C_LOG_DEBUG, "Marker 0x%02x - %s\n", c, "DRI"); // Restart Interval Definition
				// Lr : restart interval segment length (16b)
				// - specifies the length of the paramenters in the DRI segment
				v=jpegdbuf[pos++];
				v=(v<<8)|jpegdbuf[pos++];
				ijpegd_log(h, C_LOG_DEBUG, "Lr %d\n",v);
				// Ri : restart interval (16b)
				// - specifies the number of MCU in the restart interval.
				v=jpegdbuf[pos++];
				nrst=(v<<8)|jpegdbuf[pos++];//change by jmqi
				ijpegd_log(h, C_LOG_DEBUG, "Ri %d\n",v);
				break;

			case 0xe0 :/* All these markers are ignored */
			case 0xe1 :
			case 0xe2 :
			case 0xe3 :
			case 0xe4 :
			case 0xe5 :
			case 0xe6 :
			case 0xe7 :
			case 0xe8 :
			case 0xe9 :
			case 0xea :
			case 0xeb :
			case 0xec :
			case 0xed :
			case 0xee :
			case 0xef :
			case 0xf0 :
			case 0xf1 :
			case 0xf2 :
			case 0xf3 :
			case 0xf4 :
			case 0xf5 :
			case 0xf6 :
			case 0xf7 :
			case 0xf8 :
			case 0xf9 :
			case 0xfa :
			case 0xfb :
			case 0xfc :
			case 0xfd :
			case 0xfe :
				v=jpegdbuf[pos++];
				v=(v<<8)|jpegdbuf[pos++];
				v-=2;
				for(i=0;i<v;i++)
					pos++;
				break;

			default :
				ijpegd_log(h, C_LOG_DEBUG, "Unknown marker %x !\n",c);
				exit(1);
				break;
		}
	}

	ijpegd_log(h, C_LOG_DEBUG, "Total RST %d\n",rst);
	ijpegd_log(h, C_LOG_DEBUG, "Total zeros %d\n",nm);

	unsigned int min0, min1, min2, min3;
	for(i = 0; i < 64;) {
		v=h->min[i++]&1;
		v<<=2;
		v|=h->min[i++]&3;
		v<<=3;
		v|=h->min[i++]&7;
		min3 = (v>>2) & 0xF;
		//ijpegd_log(h, C_LOG_DEBUG, fpo,"%x",v>>2);
		v<<=4;
		v|=h->min[i++]&15;
		v<<=5;
		v|=h->min[i++]&31;
		v<<=6;
		v|=h->min[i++]&63;
		v<<=7;
		v|=h->min[i++]&127;
		v<<=8;
		v|=h->min[i++]&255;
		min2 = v & 0xFFFFFFFF;
		//ijpegd_log(h, C_LOG_DEBUG, fpo,"%08x",v);
		v=h->min[i++]&255;
		v<<=8;
		v|=h->min[i++]&255;
		v<<=8;
		v|=h->min[i++]&255;
		v<<=8;
		v|=h->min[i++]&255;
		//ijpegd_log(h, C_LOG_DEBUG, fpo,"%08x",v);
		min1 = v & 0xFFFFFFFF;
		v=h->min[i++]&255;
		v<<=8;
		v|=h->min[i++]&255;
		v<<=8;
		v|=h->min[i++]&255;
		v<<=8;
		v|=h->min[i++]&255;
		min0 = v & 0xFFFFFFFF;
		//ijpegd_log(h, C_LOG_DEBUG, fpo,"%08x\n",v);

		h->huffmin[i / 4 - 4] = min0;
		h->huffmin[i / 4 - 3] = min1;
		h->huffmin[i / 4 - 2] = min2;
		h->huffmin[i / 4 - 1] = min3;
	}

	for(i = 0; i < 64; i++) // hea
		h->hea[i] = h->hea[i] & 0x1FF;
	for(i = 0; i < 336; i++)
		h->heb[i] = h->heb[i] & 0xFF;
	/* Outputs the register table */
	int reg_val;
	reg_val = ((ncol-1) & 0x3);
	if(nrst != 0)
		reg_val |= 0x4;
	h->regs[0] = reg_val;

	reg_val = ((width+15)>>4)*((height+15)>>4) -1;
	h->regs[1] = reg_val;

	if(nrst != 0)
		h->regs[2] = nrst - 1;
	else
		h->regs[2] = 0;

	for(i=0; i<4; i++){
		reg_val = (nblk[i] & 0x3) * ((nblk[i] & 0x30)>>4) - 1;
		reg_val = (reg_val & 0xF)<<4;
		reg_val |= qt_sel[i]<<2;
		reg_val |= (ha_sel[i] & 0x1)<<1;
		reg_val |= (hd_sel[i] & 0x1)<<0;
		h->regs[i + 3] = reg_val;
	}

	return 0;
}
